package crud

import (
	"codeless/config"
	"codeless/dao"
	models "codeless/model"
	"codeless/utils"
	"errors"
	"github.com/gin-gonic/gin"
	"reflect"
	"strings"
)

var processMap = map[string]func(cfg *models.CrudInfo, ctx *gin.Context, node string){}
var innerProcessMap = map[string]func(cfg *models.CrudInfo, argMap map[string]interface{}, node string) error{}

func registerInnerProcess(name string, handler func(cfg *models.CrudInfo, argMap map[string]interface{}, node string) error) {
	name = strings.ToLower(name)
	innerProcessMap[name] = handler
}
func registerProcess(name string, handler func(cfg *models.CrudInfo, ctx *gin.Context, node string)) {
	name = strings.ToLower(name)
	processMap[name] = handler
}
func init() {
	registerProcess("page", pageProcess)
	registerProcess("list", listProcess)
	registerProcess("get", getProcess)
	registerProcess("add", addProcess)
	registerProcess("update", updateProcess)
	registerProcess("del", delProcess)

	registerInnerProcess("add", addInnerProcess)
	registerInnerProcess("update", updateInnerProcess)
	registerInnerProcess("del", delInnerProcess)
}
func Struct2Map(obj interface{}) map[string]interface{} {
	t := reflect.TypeOf(obj)
	v := reflect.ValueOf(obj)

	var data = make(map[string]interface{})
	for i := 0; i < t.NumField(); i++ {
		tagName := t.Field(i).Tag.Get("json")
		if tagName != "" && tagName != "-" {
			data[tagName] = v.Field(i).Interface()
		}
	}
	return data
}
func DealProcess(code string, ctx *gin.Context) {
	engine := models.NewCrudEngine(code)
	server := config.GetAppConfig().Server
	cfg, cfgOk := models.LoadCrudFile(engine.Name, server.Prod())
	if !cfgOk {
		ctx.String(401, "参数非法，权限不存在")
		return
	}
	processFn, ok := processMap[engine.Process]
	if !ok {
		ctx.String(401, "参数非法，权限不存在")
		return
	}
	processFn(cfg, ctx, engine.Node)
}
func dealInnerProcess(code string, dbData map[string]interface{}) error {
	engine := models.NewCrudEngine(code)
	server := config.GetAppConfig().Server
	cfg, cfgOk := models.LoadCrudFile(engine.Name, server.Prod())
	if !cfgOk {
		return nil
	}
	processFn, ok := innerProcessMap[engine.Process]
	if !ok {
		return nil
	}
	return processFn(cfg, dbData, engine.Node)
}
func LoadAddProcess(code string, ctx *gin.Context) (*models.SqlExplain, error) {
	engine := models.NewCrudEngine(code)
	server := config.GetAppConfig().Server
	cfg, cfgOk := models.LoadCrudFile(engine.Name, server.Prod())
	if !cfgOk {
		return nil, errors.New("参数非法，权限不存在")
	}
	if engine.Process != CREATE {
		return nil, errors.New("接口错误")
	}
	add, ok := cfg.Add[engine.Node]
	if !ok || add.Disable {
		return nil, errors.New("未启用新增,请检查配置文件")
	}
	columMap := cfg.Mod.Columns
	_, dbData, validResp := add.GetFormData(columMap, ctx, true)
	if !validResp.Valid {
		return nil, errors.New(validResp.Msg)
	}
	sqlData := dao.InsertDrySql(add, dbData)
	return &sqlData, nil
}
func LoadAddProcessMap(code string, argMap map[string]interface{}) (*models.SqlExplain, error) {
	engine := models.NewCrudEngine(code)
	server := config.GetAppConfig().Server
	cfg, cfgOk := models.LoadCrudFile(engine.Name, server.Prod())
	if !cfgOk {
		return nil, errors.New("参数非法，权限不存在")
	}
	if engine.Process != CREATE {
		return nil, errors.New("接口错误")
	}
	add, ok := cfg.Add[engine.Node]
	if !ok || add.Disable {
		return nil, errors.New("未启用新增,请检查配置文件")
	}
	columMap := cfg.Mod.Columns
	_, dbData, validResp := add.GetFormDataFromMap(columMap, argMap, true)
	if !validResp.Valid {
		return nil, errors.New(validResp.Msg)
	}
	sqlData := dao.InsertDrySql(add, dbData)
	return &sqlData, nil
}
func LoadGetProcess(code string, ctx *gin.Context) (*models.SqlExplain, error) {
	engine := models.NewCrudEngine(code)
	server := config.GetAppConfig().Server
	cfg, cfgOk := models.LoadCrudFile(engine.Name, server.Prod())
	if !cfgOk {
		return nil, errors.New("参数非法，权限不存在")
	}
	if engine.Process != GET {
		return nil, errors.New("接口错误")
	}
	get, ok := cfg.Get[engine.Node]
	if !ok || get.Disable {
		return nil, errors.New("未启用查询,请检查配置文件")
	}
	msg := get.IsValid(ctx)
	if msg != "" {
		return nil, errors.New(msg)
	}
	sqlData, sqlErr := dao.GetDrySql(get.CrudQuery)
	if sqlErr != nil {
		return nil, sqlErr
	}
	return &sqlData, nil
}
func LoadGetProcessMap(code string, argMap map[string]interface{}) (*models.SqlExplain, error) {
	engine := models.NewCrudEngine(code)
	server := config.GetAppConfig().Server
	cfg, cfgOk := models.LoadCrudFile(engine.Name, server.Prod())
	if !cfgOk {
		return nil, errors.New("参数非法，权限不存在")
	}
	if engine.Process != GET {
		return nil, errors.New("接口错误")
	}
	get, ok := cfg.Get[engine.Node]
	if !ok || get.Disable {
		return nil, errors.New("未启用查询,请检查配置文件")
	}
	msg := get.IsValidMap(argMap)
	if msg != "" {
		return nil, errors.New(msg)
	}
	sqlData, sqlErr := dao.GetDrySql(get.CrudQuery)
	if sqlErr != nil {
		return nil, sqlErr
	}
	return &sqlData, nil
}
func LoadListProcess(code string, ctx *gin.Context) (*models.SqlExplain, error) {
	engine := models.NewCrudEngine(code)
	server := config.GetAppConfig().Server
	cfg, cfgOk := models.LoadCrudFile(engine.Name, server.Prod())
	if !cfgOk {
		return nil, errors.New("参数非法，权限不存在")
	}
	if engine.Process != LIST {
		return nil, errors.New("接口错误")
	}
	list, ok := cfg.List[engine.Node]
	if !ok || list.Disable {
		return nil, errors.New("未启用查询,请检查配置文件")
	}
	msg := list.IsValid(ctx)
	if msg != "" {
		return nil, errors.New(msg)
	}
	sqlData, sqlErr := dao.ListDrySql(list.CrudQuery)
	if sqlErr != nil {
		return nil, sqlErr
	}
	return &sqlData, nil
}
func LoadListProcessMap(code string, argMap map[string]interface{}) (*models.SqlExplain, error) {
	engine := models.NewCrudEngine(code)
	server := config.GetAppConfig().Server
	cfg, cfgOk := models.LoadCrudFile(engine.Name, server.Prod())
	if !cfgOk {
		return nil, errors.New("参数非法，权限不存在")
	}
	if engine.Process != LIST {
		return nil, errors.New("接口错误")
	}
	list, ok := cfg.List[engine.Node]
	if !ok || list.Disable {
		return nil, errors.New("未启用查询,请检查配置文件")
	}
	msg := list.IsValidMap(argMap)
	if msg != "" {
		return nil, errors.New(msg)
	}
	sqlData, sqlErr := dao.ListDrySql(list.CrudQuery)
	if sqlErr != nil {
		return nil, sqlErr
	}
	return &sqlData, nil
}

func LoadPageProcessMap(code string, argMap map[string]interface{}) (*models.SqlPageExplain, error) {
	engine := models.NewCrudEngine(code)
	server := config.GetAppConfig().Server
	cfg, cfgOk := models.LoadCrudFile(engine.Name, server.Prod())
	if !cfgOk {
		return nil, errors.New("参数非法，权限不存在")
	}
	if engine.Process != PAGE {
		return nil, errors.New("接口错误")
	}
	page, ok := cfg.Page[engine.Node]
	if !ok || page.Disable {
		return nil, errors.New("未启用查询,请检查配置文件")
	}
	msg := page.IsValidMap(argMap)
	if msg != "" {
		return nil, errors.New(msg)
	}
	sqlData, sqlErr := dao.PageDrySql(page)
	if sqlErr != nil {
		return nil, sqlErr
	}
	return &sqlData, nil
}

func LoadDelProcess(code string, ctx *gin.Context) (*models.SqlExplain, error) {
	engine := models.NewCrudEngine(code)
	server := config.GetAppConfig().Server
	cfg, cfgOk := models.LoadCrudFile(engine.Name, server.Prod())
	if !cfgOk {
		return nil, errors.New("参数非法，权限不存在")
	}
	if engine.Process != DELETE {
		return nil, errors.New("接口错误")
	}
	del, ok := cfg.Del[engine.Node]
	if !ok || del.Disable {
		return nil, errors.New("未启用删除,请检查配置文件")
	}
	if del.From == "" {
		return nil, errors.New("配置文件不合法")
	}
	valid := del.StrictParse(ctx)
	if !valid {
		return nil, errors.New("数据不合法")
	}
	sqlData, sqlErr := dao.DelDrySql(del)
	if sqlErr != nil {
		return nil, sqlErr
	}
	return &sqlData, nil
}
func LoadUpdateProcess(code string, ctx *gin.Context) (*models.SqlExplain, error) {
	engine := models.NewCrudEngine(code)
	server := config.GetAppConfig().Server
	cfg, cfgOk := models.LoadCrudFile(engine.Name, server.Prod())
	if !cfgOk {
		return nil, errors.New("参数非法，权限不存在")
	}
	if engine.Process != UPDATE {
		return nil, errors.New("接口错误")
	}
	update, ok := cfg.Update[engine.Node]
	if !ok || update.Disable {
		return nil, errors.New("未启用修改,请检查配置文件")
	}
	columMap := cfg.Mod.Columns
	_, dbData, validResp := update.GetFormData(columMap, ctx, false)
	if !validResp.Valid {
		return nil, errors.New(validResp.Msg)
	}
	valid := update.StrictParse(ctx)
	if !valid {
		return nil, errors.New("数据不合法")
	}
	sqlData, sqlErr := dao.UpdateDrySql(update, dbData)
	if sqlErr != nil {
		return nil, sqlErr
	}
	return &sqlData, nil
}
func LoadUpdateProcessByMap(code string, argMap map[string]interface{}) (*models.SqlExplain, error) {
	engine := models.NewCrudEngine(code)
	server := config.GetAppConfig().Server
	cfg, cfgOk := models.LoadCrudFile(engine.Name, server.Prod())
	if !cfgOk {
		return nil, errors.New("参数非法，权限不存在")
	}
	if engine.Process != UPDATE {
		return nil, errors.New("接口错误")
	}
	update, ok := cfg.Update[engine.Node]
	if !ok || update.Disable {
		return nil, errors.New("未启用修改,请检查配置文件")
	}
	columMap := cfg.Mod.Columns
	_, dbData, validResp := update.GetFormDataFromMap(columMap, argMap, false)
	if !validResp.Valid {
		return nil, errors.New(validResp.Msg)
	}
	valid := update.StrictParseFromMap(argMap)
	if !valid {
		return nil, errors.New("数据不合法")
	}
	sqlData, sqlErr := dao.UpdateDrySql(update, dbData)
	if sqlErr != nil {
		return nil, sqlErr
	}
	return &sqlData, nil
}
func pageProcess(crudInfo *models.CrudInfo, ctx *gin.Context, node string) {
	page, ok := crudInfo.Page[node]
	if !ok {
		ctx.String(200, "未启用查询,请检查配置文件")
		return
	}
	msg := page.IsValid(ctx)
	if msg != "" {
		ctx.String(200, msg)
		return
	}
	pageIndex := utils.ParseIntDefault(ctx.PostForm("pageIndex"), 1)
	pageSize := utils.ParseIntDefault(ctx.PostForm("pageSize"), 10)
	tableJson, sqlErr := dao.PageSql(page, pageIndex, pageSize)
	if sqlErr != nil {
		ctx.String(200, sqlErr.Error())
		return
	}
	ctx.JSON(200, tableJson)

}
func getProcess(crudInfo *models.CrudInfo, ctx *gin.Context, node string) {
	get, ok := crudInfo.Get[node]
	if !ok || get.Disable {
		ctx.String(200, "未启用查询,请检查配置文件")
		return
	}
	msg := get.IsValid(ctx)
	if msg != "" {
		ctx.String(200, msg)
		return
	}
	tableJson, sqlErr := dao.GetSql(get.CrudQuery)
	if sqlErr != nil {
		ctx.String(200, sqlErr.Error())
		return
	}
	if get.Plain {
		ctx.JSON(200, tableJson)
	} else {
		rest := models.RestResult{
			Code:    0,
			Message: "",
			Result:  tableJson,
		}
		ctx.JSON(200, rest)
	}

}
func listProcess(crudInfo *models.CrudInfo, ctx *gin.Context, node string) {
	list, ok := crudInfo.List[node]
	if !ok {
		ctx.String(200, "未启用查询,请检查配置文件")
		return
	}
	msg := list.IsValid(ctx)
	if msg != "" {
		ctx.String(200, msg)
		return
	}
	if list.Plain {
		tableJson, sqlErr := dao.ListSql(list.CrudQuery)
		if sqlErr != nil {
			ctx.String(200, sqlErr.Error())
			return
		}
		ctx.JSON(200, tableJson)
	} else {
		tableJson, sqlErr := dao.ListSql(list.CrudQuery)
		if sqlErr != nil {
			ctx.String(200, sqlErr.Error())
			return
		}
		rest := models.RestResult{}
		rest.Result = tableJson
		ctx.JSON(200, tableJson)
	}

}
func addProcess(crudInfo *models.CrudInfo, ctx *gin.Context, node string) {
	var rest = models.RestResult{}
	rest.Code = 1
	add, ok := crudInfo.Add[node]
	if !ok || add.Disable {
		ctx.String(200, "未启用新增,请检查配置文件")
		return
	}
	columMap := crudInfo.Mod.Columns
	_, dbData, validResp := add.GetFormData(columMap, ctx, true)
	if !validResp.Valid {
		rest.Message = validResp.Msg
		ctx.JSON(200, rest)
		return
	}
	//判断当前数据是否存在
	for _, v := range crudInfo.Mod.Unique {
		existMap := make(map[string]interface{})
		for _, cv := range v.Columns {
			varr := strings.Split(cv, "@")
			if len(varr) == 2 {
				existMap[varr[1]] = dbData[varr[1]]
			} else {
				existMap[cv] = dbData[cv]
			}
		}
		existFlag, _ := dao.Exists(crudInfo.Mod.Table.Name, existMap)
		if existFlag {
			rest.Message = v.Tip
			ctx.JSON(200, rest)
			return
		}
	}
	//先判断是否有附件
	for k, addCol := range add.Columns {
		sitem, sok := columMap[k]
		if sok && strings.HasPrefix(sitem.Format, "@file") {
			fh, err := ctx.FormFile(k)
			if err != nil {
				if addCol.Rule == "required" {
					rest.Message = "获取" + sitem.ZhName + "文件失败"
					ctx.JSON(200, rest)
					return
				} else {
					continue
				}
			}
			fileId := utils.NewId()
			fsvr := FileService{}
			bfile, uerr := fsvr.Upload(fh, fileId, fileId)
			if uerr != nil {
				rest.Message = uerr.Error()
				ctx.JSON(200, rest)
				return
			}
			formatSp := strings.Split(sitem.Format, "|")
			if formatSp[0] == "@fileid" {
				dbData[k] = fileId
				if len(formatSp) == 2 {
					dbData[formatSp[1]] = bfile.RemoteUrl
				}
			} else if formatSp[0] == "@fileurl" {
				dbData[k] = bfile.RemoteUrl
				if len(formatSp) == 2 {
					dbData[formatSp[1]] = fileId
				}
			}
		}
	}

	beforeErr := ProcessAddBefore(add.Before, dbData)
	if beforeErr != nil {
		rest.Message = beforeErr.Error()
		ctx.JSON(200, rest)
		return
	}
	_, serr := dao.InsertSql(add, dbData)
	if serr == nil {
		afterErr := ProcessAddAfter(add.After, dbData)
		if afterErr != nil {
			rest.Message = afterErr.Error()
			ctx.JSON(200, rest)
			return
		}
		rest.Code = 0
		rest.Message = "添加成功"
	} else {
		rest.Message = "添加失败"
	}
	ctx.JSON(200, rest)
}
func addInnerProcess(crudInfo *models.CrudInfo, dbData map[string]interface{}, node string) error {
	var rest = models.RestResult{}
	rest.Code = 1
	add, ok := crudInfo.Add[node]
	if !ok || add.Disable {
		return errors.New("未启用新增,请检查配置文件")
	}
	beforeErr := ProcessAddBefore(add.Before, dbData)
	if beforeErr != nil {
		return beforeErr
	}
	_, serr := dao.InsertSql(add, dbData)
	if serr == nil {
		afterErr := ProcessAddAfter(add.After, dbData)
		if afterErr != nil {
			return afterErr
		}
		return nil
	} else {
		return errors.New("添加失败")
	}
}
func updateProcess(crudInfo *models.CrudInfo, ctx *gin.Context, node string) {
	var rest = models.RestResult{}
	rest.Code = 1
	update, ok := crudInfo.Update[node]
	if !ok || update.Disable {
		ctx.String(200, "未启用修改,请检查配置文件")
		return
	}
	columMap := crudInfo.Mod.Columns
	formData, dbData, validResp := update.GetFormData(columMap, ctx, false)
	if !validResp.Valid {
		rest.Message = validResp.Msg
		ctx.JSON(200, rest)
		return
	}
	valid := update.StrictParse(ctx)
	if !valid {
		rest.Message = "数据不合法"
		ctx.JSON(200, rest)
		return
	}
	if len(update.Select) >= 1 {
		orgInfo, sqlErr := dao.GetSql(update.CrudQuery)
		if sqlErr != nil {
			rest.Message = sqlErr.Error()
			ctx.JSON(200, rest)
			return
		}
		if len(orgInfo) == 0 {
			rest.Message = "数据获取失败或已不存在"
			ctx.JSON(200, rest)
			return
		}
		//判断当前数据是否存在
		for _, v := range crudInfo.Mod.Unique {
			existMap := make(map[string]interface{})
			checkFlag := false
			for _, sv := range v.Columns {
				varr := strings.Split(sv, "@")
				if len(varr) == 2 {
					existMap[varr[1]] = orgInfo[varr[1]]
				} else {
					existMap[sv] = formData[sv]
				}
				if orgInfo[sv] != formData[sv] {
					checkFlag = true
				}
			}
			if checkFlag {
				existFlag, _ := dao.Exists(update.From, existMap)
				if existFlag {
					rest.Message = v.Tip
					ctx.JSON(200, rest)
					return
				}
			}
		}
	}
	//先判断是否有附件
	for k, _ := range update.Columns {
		sitem, sok := columMap[k]
		if sok && strings.HasPrefix(sitem.Format, "@file") {
			fh, err := ctx.FormFile(k)
			if err == nil {
				fileId := utils.NewId()
				fsvr := FileService{}
				bfile, uerr := fsvr.Upload(fh, fileId, fileId)
				if uerr != nil {
					rest.Message = uerr.Error()
					ctx.JSON(200, rest)
					return
				}
				formatSp := strings.Split(sitem.Format, "|")
				if formatSp[0] == "@fileid" {
					dbData[k] = fileId
					if len(formatSp) == 2 {
						dbData[formatSp[1]] = bfile.RemoteUrl
					}
				} else if formatSp[0] == "@fileurl" {
					dbData[k] = bfile.RemoteUrl
					if len(formatSp) == 2 {
						dbData[formatSp[1]] = fileId
					}
				}
			} else {
				//对于修改未上传附件，删除该字段的修改，防止更新为空
				delete(dbData, k)
			}
		}
	}
	beforeErr := ProcessUpdateBefore(update.Before, dbData)
	if beforeErr != nil {
		rest.Message = beforeErr.Error()
		ctx.JSON(200, rest)
		return
	}
	_, serr := dao.UpdateSql(update, dbData)
	if serr == nil {
		afterErr := ProcessUpdateAfter(update.After, dbData)
		if afterErr != nil {
			rest.Message = afterErr.Error()
			ctx.JSON(200, rest)
			return
		}
		rest.Code = 0
		rest.Message = "修改成功"
	} else {
		rest.Message = "修改失败"
	}
	ctx.JSON(200, rest)
}
func updateInnerProcess(crudInfo *models.CrudInfo, dbData map[string]interface{}, node string) error {
	var rest = models.RestResult{}
	rest.Code = 1
	update, ok := crudInfo.Update[node]
	if !ok || update.Disable {
		return errors.New("未启用修改,请检查配置文件")
	}

	if len(update.Select) >= 1 {
		orgInfo, sqlErr := dao.GetSql(update.CrudQuery)
		if sqlErr != nil {
			return sqlErr
		}
		if len(orgInfo) == 0 {
			return errors.New("数据获取失败或已不存在")
		}
	}
	valid := update.StrictParseFromMap(dbData)
	if !valid {
		return errors.New("数据不合法")
	}
	beforeErr := ProcessUpdateBefore(update.Before, dbData)
	if beforeErr != nil {
		return beforeErr
	}
	_, serr := dao.UpdateSql(update, dbData)
	if serr == nil {
		afterErr := ProcessUpdateAfter(update.After, dbData)
		if afterErr != nil {
			return afterErr
		}
		return nil
	} else {
		return errors.New("修改失败")
	}
}
func delProcess(crudInfo *models.CrudInfo, ctx *gin.Context, node string) {
	var rest = models.RestResult{}
	rest.Code = 1
	del, ok := crudInfo.Del[node]
	if !ok || del.Disable {
		ctx.String(200, "未启用删除,请检查配置文件")
		return
	}
	if del.From == "" {
		rest.Message = "配置文件不合法"
		ctx.JSON(200, rest)
		return
	}
	valid := del.StrictParse(ctx)
	if !valid {
		rest.Message = "数据不合法"
		ctx.JSON(200, rest)
		return
	}

	if len(del.Select) != 0 {
		orgInfo, sqlErr := dao.GetSql(del.CrudQuery)
		if sqlErr != nil {
			rest.Message = sqlErr.Error()
			ctx.JSON(200, rest)
			return
		}
		if len(orgInfo) == 0 {
			rest.Message = "数据获取失败或已不存在"
			ctx.JSON(200, rest)
			return
		}
	}
	beforeErr := ProcessDelBefore(del.Before, del.GetArgValMap())
	if beforeErr != nil {
		rest.Message = beforeErr.Error()
		ctx.JSON(200, rest)
		return
	}
	db, _ := dao.DelSql(del)
	if db == 1 {

		afterErr := ProcessDelAfter(del.After, del.GetArgValMap())
		if afterErr != nil {
			rest.Message = afterErr.Error()
			ctx.JSON(200, rest)
			return
		}
		rest.Code = 0
		rest.Message = "删除成功"
	} else {
		rest.Message = "删除失败"
	}
	ctx.JSON(200, rest)
}
func delInnerProcess(crudInfo *models.CrudInfo, dbData map[string]interface{}, node string) error {
	var rest = models.RestResult{}
	rest.Code = 1
	del, ok := crudInfo.Del[node]
	if !ok || del.Disable {
		return errors.New("未启用删除,请检查配置文件")
	}
	if del.From == "" {
		return errors.New("配置文件不合法")
	}
	valid := del.StrictParseFromMap(dbData)
	if !valid {
		return errors.New("数据不合法")
	}
	if len(del.Select) != 0 {
		orgInfo, sqlErr := dao.GetSql(del.CrudQuery)
		if sqlErr != nil {
			return sqlErr
		}
		if len(orgInfo) == 0 {
			return errors.New("数据获取失败或已不存在")
		}
	}
	beforeErr := ProcessDelBefore(del.Before, dbData)
	if beforeErr != nil {
		return beforeErr
	}
	db, _ := dao.DelSql(del)
	if db == 1 {
		afterErr := ProcessDelAfter(del.After, dbData)
		if afterErr != nil {
			return afterErr
		}
		return nil
	} else {
		return errors.New("删除失败")
	}
}
